package com.ccrfid.rmc6.resource.sse;

import com.ccrfid.rmc6.appconfig.exception.pojo.AppException;
import com.ccrfid.rmc6.appconfig.exception.pojo.CustomErrors;
import com.ccrfid.rmc6.entity.personnel.PersonnelView;
import com.ccrfid.rmc6.entity.poi.PoiLocationEntity;
import com.ccrfid.rmc6.lbservice3.client.SseClient;
import com.ccrfid.rmc6.lbservice3.client.WebClient;
import com.ccrfid.rmc6.lbservice3.manager.ClientManager;
import com.ccrfid.rmc6.lbservice3.pojo.locate.PersonnelTrackInfo;
import com.ccrfid.rmc6.resource.auth.pojo.UserPrincipal;
import com.ccrfid.rmc6.resource.sse.pojo.SubscribeLocation;
import com.ccrfid.rmc6.service.auth.AuthenticationService;
import com.ccrfid.rmc6.service.device.LocatorService;
import com.ccrfid.rmc6.service.personnel.PersonnelService;
import com.ccrfid.rmc6.service.poi.PoiService;
import com.ccrfid.rmc6.util.NetUtil;
import org.glassfish.jersey.media.sse.EventOutput;
import org.glassfish.jersey.media.sse.OutboundEvent;
import org.glassfish.jersey.media.sse.SseFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Path("sse")
public class SseResource {
    private static final Logger logger = LoggerFactory.getLogger(SseResource.class);
    @Context
    private HttpServletRequest httpServletRequest;
    @Autowired
    private ClientManager clientManager;
    @Autowired
    private PoiService poiService;
    @Autowired
    private PersonnelService personnelService;
    @Autowired
    private LocatorService locatorService;
    @Autowired
    private AuthenticationService authenticationService;

    @GET
    @Path("channel/{clientId}")
    @Produces(SseFeature.SERVER_SENT_EVENTS)
    public EventOutput getSseChannel(@PathParam("clientId") String clientId) throws IOException {
        SseClient sseClient = (SseClient) clientManager.getWebClient(clientId);
        if (sseClient != null) {
            return sseClient.getEventOutput();
        } else {
            EventOutput eventOutput = new EventOutput();
            String clientIp = httpServletRequest.getRemoteAddr();
            List<String> clientIpList = new ArrayList<>();
            clientIpList.add(clientIp);
            if (clientIp.equals("127.0.0.1") || clientIp.equals("0:0:0:0:0:0:0:1")) {
                Set<String> localIpSet = NetUtil.getLocalIpAddresses();
                if (localIpSet.size() > 0) {
                    for (String ip : localIpSet) {
                        clientIpList.add(ip);
                    }
                }
            }
            clientManager.addWebClient(new SseClient(clientId, null, clientIpList, eventOutput));
            OutboundEvent.Builder builder = new OutboundEvent.Builder();
            builder.data("");
            eventOutput.write(builder.build());
            return eventOutput;
        }
    }

    @GET
    @Path("channel/{clientId}/token/{token}")
    @Produces(SseFeature.SERVER_SENT_EVENTS)
    public EventOutput getSseChannel(@PathParam("clientId") String clientId, @PathParam("token") String token)
            throws IOException, AppException {
        SseClient sseClient = (SseClient) clientManager.getWebClient(clientId);
        if (sseClient != null) {
            return sseClient.getEventOutput();
        } else {
            if (authenticationService.validateToken(token)) {
                EventOutput eventOutput = new EventOutput();
                String clientIp = httpServletRequest.getRemoteAddr();
                List<String> clientIpList = new ArrayList<>();
                clientIpList.add(clientIp);
                if (clientIp.equals("127.0.0.1") || clientIp.equals("0:0:0:0:0:0:0:1")) {
                    Set<String> localIpSet = NetUtil.getLocalIpAddresses();
                    if (localIpSet.size() > 0) {
                        for (String ip : localIpSet) {
                            clientIpList.add(ip);
                        }
                    }
                }
                UserPrincipal userPrincipal = authenticationService.getUserPrincipal(token);
                String userAccount = userPrincipal.getUserAccount();
                clientManager.addWebClient(new SseClient(clientId, userAccount, clientIpList, eventOutput));
                logger.debug("SSE -- addWebClient, clientId: {}, userAccount:{}, clientIp: {}", clientId, userAccount, clientIp);
                OutboundEvent.Builder builder = new OutboundEvent.Builder();
                builder.data("");
                eventOutput.write(builder.build());
                return eventOutput;
            } else {
                throw new AppException(Response.Status.UNAUTHORIZED.getStatusCode(), 999, "认证token无效.", null);
            }
        }
    }

    @PUT
    @Path("channel/{clientId}/subscription")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Boolean putSubscription(@PathParam("clientId") String clientId, SubscribeLocation sub) {
        List<PoiLocationEntity> list =
                poiService.getPoiLocationsInPolygon(sub.getMapLevel(), sub.getFloorNumber(), sub.getPolygon());
        clientManager.updateWebClientViewRange(clientId, list);
        return true;
    }

    @PUT
    @Path("channel/{clientId}/single-location-subscription/{locationId}")
    @Produces(MediaType.APPLICATION_JSON)
    public Boolean putSingleLocationSubscription(@PathParam("clientId") String clientId,
                                                 @PathParam("locationId") Long locationId) {
        clientManager.updateWebClientViewRange(clientId, locationId);
        return true;
    }

    @PUT
    @Path("channel/{clientId}/tracking/{personnelId}")
    @Produces(MediaType.APPLICATION_JSON)
    public PersonnelTrackInfo putPersonnelTracking(@PathParam("clientId") String clientId,
                                                   @PathParam("personnelId") Long personnelId) throws AppException {
        //检查参数
        PersonnelView personnelView = checkParam(clientId, personnelId);
        //开启追踪
        clientManager.addTrack(personnelId, clientId);
        //返回位置
        if (personnelView.getPos() != null) {
            PersonnelTrackInfo trackInfo = new PersonnelTrackInfo(personnelId,
                    personnelView.getLocatorAddress(), personnelView.getPos(), personnelView.getFloorNumber());
            trackInfo.setLastLocateTime(personnelView.getLastLocateTime());
            PoiLocationEntity locationEntity =
                    poiService.get3LevelLocationByPos(personnelView.getFloorNumber(), personnelView.getPos());
            if (locationEntity != null) {
                trackInfo.setLocationId(locationEntity.getId());
                trackInfo.setLocationName(locationEntity.getLocationName());
            }
            return trackInfo;
        } else {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.person_not_located.getCode(), CustomErrors.person_not_located.getReason(), "");
        }
    }

    private PersonnelView checkParam(String clientId, Long personnelId) throws AppException {
        WebClient webClient = clientManager.getWebClient(clientId);
        if (webClient == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.client_not_found.getCode(), CustomErrors.client_not_found.getReason(), "");
        }
        PersonnelView personnelView = personnelService.getPersonnelDetailById(personnelId);
        if (personnelView != null) {
            if (personnelView.getCardNumber() == null) {
                throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                        CustomErrors.person_not_bind_card.getCode(), CustomErrors.person_not_bind_card.getReason(), "");
            }
            if (personnelView.getPos() == null) {
                throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                        CustomErrors.person_not_located.getCode(), CustomErrors.person_not_located.getReason(), "");
            }
        } else {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.person_not_found.getCode(), CustomErrors.person_not_found.getReason(), "");
        }
        return personnelView;
    }

    @DELETE
    @Path("channel/{clientId}/tracking/{personnelId}")
    public void deletePersonnelTracking(@PathParam("clientId") String clientId,
                                        @PathParam("personnelId") Long personnelId) {
        clientManager.deleteTrack(personnelId, clientId);
    }

    @PUT
    @Path("channel/{clientId}/door-{doorId}-subscription")
    public Boolean updateDoorSubscription(@PathParam("clientId") String clientId,
                                          @PathParam("doorId") Long doorId) {
        clientManager.updateDoorSubscription(clientId, doorId);
        return true;
    }

}
